/*
 * ili9341.c
 *
 *  Created on: 2021年11月8日
 *      Author: master
 */

/**
 * @file ili9341.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "ili9341.h"

/*********************
 *      DEFINES
 *********************/
#define TAG "ILI9341"

/**********************
 *      TYPEDEFS
 **********************/

/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */
typedef struct
{
	uint8_t cmd;
	uint8_t data[16];
	uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;
volatile bool isFinish = false;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void ili9341_set_orientation(uint8_t orientation);

static void ili9341_send_cmd(uint8_t cmd);
static void ili9341_send_data(void *data, uint16_t length);
static void ili9341_send_color(const uint8_t *data, uint16_t size);

static void SPI_UserCallBack(SPI_Type *base, spi_master_handle_t *handle, status_t status, void *userData);
/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void ili9341_init(void)
{
	lcd_init_cmd_t ili_init_cmds[]={
		{0xCF, {0x00, 0xD9, 0X30}, 3},
		{0xED, {0x64, 0x03, 0X12, 0X81}, 4},
		{0xE8, {0x85, 0x01, 0x78}, 3},
		{0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
		{0xF7, {0x20}, 1},
		{0xEA, {0x00, 0x00}, 2},
		{0xC0, {0x21}, 1},          /*Power control*/
		{0xC1, {0x12}, 1},          /*Power control */
		{0xC5, {0x32, 0x3C}, 2},    /*VCOM control*/
		{0xC7, {0xC1}, 1},          /*VCOM control*/
		{0x36, {0x08}, 1},          /*Memory Access Control*/
		{0x3A, {0x55}, 1},			/*Pixel Format Set*/
		{0xB1, {0x00, 0x18}, 2},
		{0xB6, {0x0A, 0xA2}, 2},
		{0xF2, {0x00}, 1},
		{0x26, {0x01}, 1},
		{0xE0, {0x0F, 0x20, 0x1E, 0x09, 0x12, 0x0B, 0x50, 0XBA, 0x44, 0x09, 0x14, 0x05, 0x23, 0x21, 0x00}, 15},
		{0XE1, {0x00, 0x19, 0x19, 0x00, 0x12, 0x07, 0x2D, 0x28, 0x3F, 0x02, 0x0A, 0x08, 0x25, 0x2D, 0x0F}, 15},
		{0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
		{0x2B, {0x00, 0x00, 0x01, 0x3f}, 4},
		{0x2C, {0}, 0},
		{0xB7, {0x07}, 1},
		{0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
		{0x11, {0}, 0x80},
		{0x29, {0}, 0x80},
		{0, {0}, 0xff},
	};

	GPIO_PinWrite(BOARD_INITPINS_LCD_BL_GPIO, BOARD_INITPINS_LCD_BL_PORT, BOARD_INITPINS_LCD_BL_PIN, 1);  //打开LCD背光
#if ILI9341_USE_RST
	GPIO_PinWrite(BOARD_INITPINS_LCD_RESET_GPIO, BOARD_INITPINS_LCD_RESET_PORT, BOARD_INITPINS_LCD_RESET_PIN, 0);  //复位LCD
	SDK_DelayAtLeastUs(50000, CLOCK_GetCoreSysClkFreq());
	GPIO_PinWrite(BOARD_INITPINS_LCD_RESET_GPIO, BOARD_INITPINS_LCD_RESET_PORT, BOARD_INITPINS_LCD_RESET_PIN, 1);
	SDK_DelayAtLeastUs(100000, CLOCK_GetCoreSysClkFreq());
#endif
	//Send all the commands
	uint16_t cmd = 0;
	while (ili_init_cmds[cmd].databytes != 0xff)
	{
		ili9341_send_cmd(ili_init_cmds[cmd].cmd);
		ili9341_send_data(ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes & 0x1F);
		if (ili_init_cmds[cmd].databytes & 0x80)
		{
			SDK_DelayAtLeastUs(1000, CLOCK_GetCoreSysClkFreq());
		}
		cmd++;
	}

	ili9341_set_orientation(2);

#if ILI9341_INVERT_COLORS == 0
	ili9341_send_cmd(0x21);
#else
	ili9341_send_cmd(0x20);
#endif
}

void ili9341_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
	uint8_t data[4];
	/*Column addresses*/
	ili9341_send_cmd(0x2A);
	data[0] = (area->x1 >> 8) & 0xFF;
	data[1] = area->x1 & 0xFF;
	data[2] = (area->x2 >> 8) & 0xFF;
	data[3] = area->x2 & 0xFF;
	ili9341_send_data(data, 4);

	/*Page addresses*/
	ili9341_send_cmd(0x2B);
	data[0] = (area->y1 >> 8) & 0xFF;
	data[1] = area->y1 & 0xFF;
	data[2] = (area->y2 >> 8) & 0xFF;
	data[3] = area->y2 & 0xFF;
	ili9341_send_data(data, 4);

	/*Memory write*/
	ili9341_send_cmd(0x2C);
	uint32_t size = lv_area_get_width(area) * lv_area_get_height(area);
	ili9341_send_color((uint8_t *)color_map, size);
}

void ili9341_sleep_in()
{
	uint8_t data[] = { 0x08 };
	ili9341_send_cmd(0x10);
	ili9341_send_data(&data, 1);
}

void ili9341_sleep_out()
{
	uint8_t data[] = { 0x08 };
	ili9341_send_cmd(0x11);
	ili9341_send_data(&data, 1);
}
uint8_t rx_data;
spi_master_handle_t spi_handle;
spi_transfer_t xfer;
uint8_t tx_data;
void ili9342_read_id()
{
	tx_data = 0xD3;
	xfer.rxData = &rx_data;
	xfer.txData = &tx_data;
	xfer.dataSize = 1;
	LCD_MODLE(0);
	SPI_MasterTransferCreateHandle(SPI8, &spi_handle, SPI_UserCallBack, NULL);
	SPI_MasterTransferNonBlocking(SPI8, &spi_handle,&xfer);
	//LCD_CS(1);
	LCD_MODLE(1);
	//SDK_DelayAtLeastUs(1000, CLOCK_GetCoreSysClkFreq());
	for(int i = 0;i < 3;i++)
	{
		LCD_CS(0);
		tx_data =  0xff;
		SPI_MasterTransferNonBlocking(SPI8, &spi_handle,&xfer);
		while(!isFinish){};
		isFinish = false;
	}
	PRINTF("SendFinish\r\n");
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static void SPI_UserCallBack(SPI_Type *base, spi_master_handle_t *handle, status_t status, void *userData)
{
	isFinish = true;
	PRINTF("txdata:%X\r\n",xfer.txData[0]);
	PRINTF("rxdata:%X\r\n",xfer.rxData[0]);
}

static void ili9341_send_cmd(uint8_t cmd)
{
	spi_transfer_t xfer;
	xfer.txData = &cmd;
	xfer.rxData = NULL;
	xfer.dataSize  = 1;
	LCD_MODLE(0);
	LCD_CS(0);
	SPI_MasterTransferBlocking(SPI8,&xfer);
	LCD_CS(1);
}

static void ili9341_send_data(void *data, uint16_t length)
{
	spi_transfer_t xfer;
	xfer.txData = data;
	xfer.rxData = NULL;
	xfer.dataSize  = length;
	LCD_MODLE(1);
	LCD_CS(0);
	SPI_MasterTransferBlocking(SPI8,&xfer);
	LCD_CS(1);
}

static void ili9341_send_color(const uint8_t *data, uint16_t size)
{
	uint8_t *t_data = lv_mem_alloc(sizeof(uint8_t) * size * 2); //两个字节标识一个像素点
	lv_memset(t_data,0x00,size * 2);
	//uint8_t t_data[2];
	for(int i = 0;i < size;i++)
	{
		t_data[i * 2 + 1] = *(data + i * 2);
		t_data[i * 2] = *(data + i * 2 + 1);
	}
	ili9341_send_data(t_data,size * 2);
	lv_mem_free(t_data);
}

static void ili9341_set_orientation(uint8_t orientation)
{

#if defined CONFIG_LV_PREDEFINED_DISPLAY_M5STACK
    uint8_t data[] = {0x68, 0x68, 0x08, 0x08};
#elif defined (CONFIG_LV_PREDEFINED_DISPLAY_M5CORE2)
	uint8_t data[] = {0x08, 0x88, 0x28, 0xE8};
#elif defined (CONFIG_LV_PREDEFINED_DISPLAY_WROVER4)
    uint8_t data[] = {0x6C, 0xEC, 0xCC, 0x4C};
#elif defined (CONFIG_LV_PREDEFINED_DISPLAY_NONE)
    uint8_t data[] = {0x48, 0x88, 0x28, 0xE8};
#endif

    ili9341_send_cmd(0x36);
    ili9341_send_data((void *) &data[orientation], 1);
}
